package bsql

import (
	"context"
	"database/sql"
	"gitee.com/yyangl/bsql/scan"
	_ "github.com/go-sql-driver/mysql"
	"time"
)

type Db struct {
	db *sql.DB
	duration time.Duration
}

func (db *Db)SetTimeOut(d time.Duration) {
	db.duration = d
}

func Open(dns string)(*Db,error) {
	db,err := sql.Open("mysql",dns)
	if err != nil {
		return nil,err
	}
	if err = db.Ping();err != nil {
		return nil,err
	}
	return &Db{db: db},nil
}

// SetMaxIdleConns 最大空闲连接数
func (db *Db)SetMaxIdleConns(n int)  {
	db.db.SetMaxIdleConns(n)
}

// SetConnMaxLifetime 最大连接时间
func (db *Db)SetConnMaxLifetime(d time.Duration)  {
	db.db.SetConnMaxLifetime(d)
}

// SetMaxOpenConns 最大连接数
func (db *Db)SetMaxOpenConns(n int)  {
	db.db.SetMaxOpenConns(n)
}

// SetConnMaxIdleTime 最大空闲连接时间
func (db *Db)SetConnMaxIdleTime(d time.Duration)  {
	db.db.SetConnMaxIdleTime(d)
}

func (db *Db)RunInTransaction(fn func(tx *Tx)error)error  {
	ctx,cancel := context.WithTimeout(context.Background(),db.duration)
	defer cancel()
	ops := Options{sql.TxOptions{
		Isolation: 0,
		ReadOnly:  false,
	}}
	return db.RunInTransactionT(ctx,ops,fn)
}

func (db *Db)RunInTransactionT(ctx context.Context, ops Options, fn func(tx *Tx) error) error {
	tx,err := db.db.BeginTx(ctx,&ops.TxOptions)
	if err != nil {
		return err
	}
	t := &Tx{tx}
	if err = fn(t);err != nil {
		_ = t.Rollback()
	}
	return t.Commit()
}

// Close 最大空闲连接数
func (db *Db)Close() error {
	return db.db.Close()
}

func (db *Db) Query(data interface{},query string, args ...interface{})error {
	ctx,cancel := context.WithTimeout(context.Background(),db.duration)
	defer cancel()
	return db.QueryT(ctx,data,query,args...)
}

func (db *Db) QueryT(ctx context.Context, data interface{}, query string, args ...interface{}) error {
	rows,err := db.db.QueryContext(ctx,query,args...)
	if err != nil {
		return err
	}
	return scan.Scan(rows,data)
}

func (db *Db) Exec(query string, args ...interface{}) (*Result, error) {
	ctx,cancel := context.WithTimeout(context.Background(),db.duration)
	defer cancel()
	return db.ExecT(ctx,query,args...)
}

func (db *Db) ExecT(ctx context.Context, query string, args ...interface{}) (*Result, error) {
	result,err := db.db.ExecContext(ctx,query,args...)
	if err != nil {
		return nil,err
	}
	return &Result{result},nil
}